source('../settings/settings.R')
source('commonFunctions.R')
library(nlme)
set.seed(43)
drive1 <- read.csv('../data/processed/analysis/TT1_Drive_1_PP.csv')
drive2 <- read.csv('../data/processed/Analysis/TT1_Drive_2_PP.csv')
drive3 <- read.csv('../data/processed/Analysis/TT1_Drive_3_PP.csv')
drive4 <- read.csv('../data/processed/Analysis/TT1_Drive_4_PP.csv', stringsAsFactors = T)
dfSeg <- data.frame(rep(1, nrow(drive4)), rep(2, nrow(drive4)), rep(3, nrow(drive4)), rep(4, nrow(drive4)))
names(dfSeg) <- c("Seg1", "Seg2", "Seg3", "Seg4")

combinedDf_Seg1 <- cbind(drive4, 
                    drive1$MeanPP_Seg0, 
                    drive2$MeanPP_Seg1, drive3$MeanPP_Seg1, 
                    drive2$MeanPP_Seg0_1, drive3$MeanPP_Seg0_1,
                    drive2$StdPP_Seg1, drive3$StdPP_Seg1,
                    drive2$StdPP_Seg0_1, drive3$StdPP_Seg0_1,
                    drive2$MeanPP_AccHigh1, drive3$MeanPP_AccHigh1,
                    drive2$X.MeanPP_AccLow1, drive3$X.MeanPP_AccLow1,
                    drive2$StdPP_AccHigh1, drive3$StdPP_AccHigh1,
                    drive2$StdPP_AccLow1, drive3$StdPP_AccLow1,
                    dfSeg$Seg1
                  )
combinedDf_Seg2 <- cbind(drive4, 
                    drive1$MeanPP_Seg0, 
                    drive2$MeanPP_Seg2, drive3$MeanPP_Seg2, 
                    drive2$MeanPP_Seg0_2, drive3$MeanPP_Seg0_2,
                    drive2$StdPP_Seg2, drive3$StdPP_Seg2,
                    drive2$StdPP_Seg0_2, drive3$StdPP_Seg0_2,
                    drive2$MeanPP_AccHigh2, drive3$MeanPP_AccHigh2,
                    drive2$X.MeanPP_AccLow2, drive3$X.MeanPP_AccLow2,
                    drive2$StdPP_AccHigh2, drive3$StdPP_AccHigh2,
                    drive2$StdPP_AccLow2, drive3$StdPP_AccLow2,
                    dfSeg$Seg2
                  )
combinedDf_Seg3 <- cbind(drive4, 
                    drive1$MeanPP_Seg0, 
                    drive2$MeanPP_Seg3, drive3$MeanPP_Seg3, 
                    drive2$MeanPP_Seg0_3, drive3$MeanPP_Seg0_3,
                    drive2$StdPP_Seg3, drive3$StdPP_Seg3,
                    drive2$StdPP_Seg0_3, drive3$StdPP_Seg0_3,
                    drive2$MeanPP_AccHigh3, drive3$MeanPP_AccHigh3,
                    drive2$X.MeanPP_AccLow3, drive3$X.MeanPP_AccLow3,
                    drive2$StdPP_AccHigh3, drive3$StdPP_AccHigh3,
                    drive2$StdPP_AccLow3, drive3$StdPP_AccLow3,
                    dfSeg$Seg3
                  )
combinedDf_Seg4 <- cbind(drive4, 
                    drive1$MeanPP_Seg0, 
                    drive2$MeanPP_Seg4, drive3$MeanPP_Seg4, 
                    drive2$MeanPP_Seg0_4, drive3$MeanPP_Seg0_4,
                    drive2$StdPP_Seg4, drive3$StdPP_Seg4,
                    drive2$StdPP_Seg0_4, drive3$StdPP_Seg0_4,
                    drive2$MeanPP_AccHigh4, drive3$MeanPP_AccHigh4,
                    drive2$X.MeanPP_AccLow4, drive3$X.MeanPP_AccLow4,
                    drive2$StdPP_AccHigh4, drive3$StdPP_AccHigh4,
                    drive2$StdPP_AccLow4, drive3$StdPP_AccLow4,
                    dfSeg$Seg4
                  )

common_names <- c("PP_Dev_1_Turning",
                  "PP_Dev_2_Straight", "PP_Dev_3_Straight", 
                  "PP_Dev_2_Turning", "PP_Dev_3_Turning", 
                  "Std_PP_2_Straight", "Std_PP_3_Straight", 
                  "Std_PP_2_Turning", "Std_PP_3_Turning",
                  
                  "Mean_PP_2_AccHigh", "Mean_PP_3_AccHigh",
                  "Mean_PP_2_AccLow", "Mean_PP_3_AccLow",
                  "Std_PP_2_AccHigh", "Std_PP_3_AccHigh",
                  "Std_PP_2_AccLow", "Std_PP_3_AccLow",
                  
                  "Segment")

names(combinedDf_Seg1) <- c(names(drive4), common_names)
names(combinedDf_Seg2) <- c(names(drive4), common_names)
names(combinedDf_Seg3) <- c(names(drive4), common_names)
names(combinedDf_Seg4) <- c(names(drive4), common_names)

# combinedDf_Seg1$Subject <- paste0(as.factor(combinedDf_Seg1$Subject), ".S1")
# combinedDf_Seg2$Subject <- paste0(as.factor(combinedDf_Seg2$Subject), ".S2")
# combinedDf_Seg3$Subject <- paste0(as.factor(combinedDf_Seg3$Subject), ".S3")
# combinedDf_Seg4$Subject <- paste0(as.factor(combinedDf_Seg4$Subject), ".S4")

combinedDf <- rbind(combinedDf_Seg1, combinedDf_Seg2, combinedDf_Seg3, combinedDf_Seg4)

# combinedDf$Subject <- paste0("#", str_pad(combinedDf$Subject, 2, pad="0"))
combinedDf$Segment <- as.factor(combinedDf$Segment)
combinedDf$ActivityEncoded <- factor(ifelse(combinedDf$Activity == "NO", "1", ifelse(combinedDf$Activity == "C", "2", "3")))

combinedDf <- combinedDf[complete.cases(combinedDf),]
combinedDf$Subject = as.factor(combinedDf$Subject)
model = lm(PP_After ~ 
              PP_Dev_2_Straight + 
              PP_Dev_3_Straight +
              PP_Dev_2_Turning + 
              PP_Dev_3_Turning + 
              Std_PP_2_Straight + 
              Std_PP_3_Straight + 
              Std_PP_2_Turning +
              Std_PP_3_Turning +
              # PP_Prior +
              factor(ActivityEncoded),
            data=combinedDf, random = ~1|factor(Subject), method = "REML")
method = 'REML' is not supported. Using 'qr'In lm.fit(x, y, offset = offset, singular.ok = singular.ok, ...) :
 extra argument ‘random’ will be disregarded
# anova(model)
summary(model)

Call:
lm(formula = PP_After ~ PP_Dev_2_Straight + PP_Dev_3_Straight + 
    PP_Dev_2_Turning + PP_Dev_3_Turning + Std_PP_2_Straight + 
    Std_PP_3_Straight + Std_PP_2_Turning + Std_PP_3_Turning + 
    factor(ActivityEncoded), data = combinedDf, method = "REML", 
    random = ~1 | factor(Subject))

Residuals:
     Min       1Q   Median       3Q      Max 
-0.17764 -0.07137  0.00092  0.05144  0.34502 

Coefficients:
                         Estimate Std. Error t value Pr(>|t|)    
(Intercept)              -0.02294    0.04870  -0.471   0.6393    
PP_Dev_2_Straight         0.68712    0.15583   4.410 4.45e-05 ***
PP_Dev_3_Straight        -0.37734    0.23618  -1.598   0.1155    
PP_Dev_2_Turning         -0.22015    0.14683  -1.499   0.1391    
PP_Dev_3_Turning          0.38632    0.22101   1.748   0.0857 .  
Std_PP_2_Straight         0.02144    0.38757   0.055   0.9561    
Std_PP_3_Straight         0.50369    0.36818   1.368   0.1765    
Std_PP_2_Turning         -0.08343    0.50205  -0.166   0.8686    
Std_PP_3_Turning         -0.95800    0.60836  -1.575   0.1207    
factor(ActivityEncoded)2  0.08647    0.03285   2.633   0.0108 *  
factor(ActivityEncoded)3  0.17032    0.03184   5.349 1.51e-06 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.0993 on 59 degrees of freedom
Multiple R-squared:  0.5613,    Adjusted R-squared:  0.4869 
F-statistic: 7.548 on 10 and 59 DF,  p-value: 1.338e-07
plot(model)

No Random Effects

linearModel1 <- lm(PP_After ~ 
                Mean_PP_2_AccHigh
              + Mean_PP_2_AccLow
              + Mean_PP_3_AccHigh
              + Mean_PP_3_AccLow
              + Std_PP_2_AccHigh
              + Std_PP_2_AccLow
              + Std_PP_3_AccHigh
              + Std_PP_3_AccLow
              # + PP_Prior
              + factor(ActivityEncoded),
            data=combinedDf)

# anova(model)
summary(linearModel1)

Call:
lm(formula = PP_After ~ Mean_PP_2_AccHigh + Mean_PP_2_AccLow + 
    Mean_PP_3_AccHigh + Mean_PP_3_AccLow + Std_PP_2_AccHigh + 
    Std_PP_2_AccLow + Std_PP_3_AccHigh + Std_PP_3_AccLow + factor(ActivityEncoded), 
    data = combinedDf)

Residuals:
      Min        1Q    Median        3Q       Max 
-0.143483 -0.066038 -0.007552  0.051595  0.300313 

Coefficients:
                         Estimate Std. Error t value Pr(>|t|)    
(Intercept)              -0.06552    0.03640  -1.800  0.07695 .  
Mean_PP_2_AccHigh         1.63376    0.50590   3.229  0.00203 ** 
Mean_PP_2_AccLow         -1.15253    0.49763  -2.316  0.02405 *  
Mean_PP_3_AccHigh         0.74436    0.31520   2.362  0.02152 *  
Mean_PP_3_AccLow         -0.66142    0.33913  -1.950  0.05589 .  
Std_PP_2_AccHigh         -1.28974    1.45755  -0.885  0.37982    
Std_PP_2_AccLow           0.79583    1.15728   0.688  0.49435    
Std_PP_3_AccHigh          0.15278    0.94374   0.162  0.87195    
Std_PP_3_AccLow           0.91693    0.83810   1.094  0.27838    
factor(ActivityEncoded)2  0.08724    0.03041   2.869  0.00571 ** 
factor(ActivityEncoded)3  0.14810    0.03043   4.867 8.83e-06 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.09353 on 59 degrees of freedom
Multiple R-squared:  0.6108,    Adjusted R-squared:  0.5448 
F-statistic: 9.259 on 10 and 59 DF,  p-value: 5.37e-09
plot(linearModel1)

With Random Effects

linearModel1 <- lme(PP_After ~ 
                Mean_PP_2_AccHigh
              + Mean_PP_2_AccLow
              + Mean_PP_3_AccHigh
              + Mean_PP_3_AccLow
              + Std_PP_2_AccHigh
              + Std_PP_2_AccLow
              + Std_PP_3_AccHigh
              + Std_PP_3_AccLow
              # + PP_Prior
              + factor(ActivityEncoded),
            data=combinedDf, random = ~1|factor(Subject), method = "REML")

# anova(model)
summary(linearModel1)
Linear mixed-effects model fit by REML
 Data: combinedDf 

Random effects:
 Formula: ~1 | factor(Subject)
        (Intercept)     Residual
StdDev:   0.1403901 2.578859e-17

Fixed effects: PP_After ~ Mean_PP_2_AccHigh + Mean_PP_2_AccLow + Mean_PP_3_AccHigh +      Mean_PP_3_AccLow + Std_PP_2_AccHigh + Std_PP_2_AccLow + Std_PP_3_AccHigh +      Std_PP_3_AccLow + factor(ActivityEncoded) 
 Correlation: 
                         (Intr) M_PP_2_AH M_PP_2_AL M_PP_3_AH M_PP_3_AL S_PP_2_AH S_PP_2_AL S_PP_3_AH S_PP_3_AL f(AE)2
Mean_PP_2_AccHigh        -0.205                                                                                       
Mean_PP_2_AccLow          0.195 -0.962                                                                                
Mean_PP_3_AccHigh        -0.010  0.006    -0.030                                                                      
Mean_PP_3_AccLow          0.091 -0.071     0.032    -0.695                                                            
Std_PP_2_AccHigh          0.003  0.147    -0.187     0.084    -0.072                                                  
Std_PP_2_AccLow          -0.072  0.041    -0.023    -0.130     0.082    -0.873                                        
Std_PP_3_AccHigh         -0.049  0.073    -0.122    -0.450     0.305    -0.337     0.191                              
Std_PP_3_AccLow           0.039  0.193    -0.209     0.378    -0.322     0.303    -0.240    -0.525                    
factor(ActivityEncoded)2 -0.657  0.084    -0.072     0.017    -0.063    -0.060     0.081     0.022    -0.033          
factor(ActivityEncoded)3 -0.654  0.138    -0.127     0.052    -0.071    -0.027     0.027     0.052     0.028     0.433

Standardized Within-Group Residuals:
       Min         Q1        Med         Q3        Max 
-2.1525473 -0.3531523  0.0000000  0.5381368  6.4576419 

Number of Observations: 70
Number of Groups: 21 
plot(linearModel1)

Machine Learning

ppAfter <- combinedDf$PP_After
ppAfterArray <- matrix(ppAfter, nrow = 1,ncol = length(ppAfter))
  
thresholdPPAfter <- otsu(ppAfterArray, range=c(min(ppAfter), max(ppAfter))) # Expected Threshold > 0.10123
print(paste0('Threshold: ', thresholdPPAfter))
[1] "Threshold: 0.101235546875"
selectedDf <- combinedDf %>% select(
                  "Subject", "Activity", "PP_After", "PP_Prior",
                  "Mean_PP_2_AccHigh", "Mean_PP_3_AccHigh",
                  "Mean_PP_2_AccLow", "Mean_PP_3_AccLow",
                  "Std_PP_2_AccHigh", "Std_PP_3_AccHigh",
                  "Std_PP_2_AccLow", "Std_PP_3_AccLow")

selectedDf$Subject <- NULL
selectedDf$Activity_NO <- ifelse(selectedDf$Activity == "NO", 1, 0)
selectedDf$Activity_C <- ifelse(selectedDf$Activity == "C", 1, 0)
selectedDf$Activity_M <- ifelse(selectedDf$Activity == "M", 1, 0)
selectedDf$Activity <- NULL

# selectedDf$PP_Dev_1_Turning <- NULL
# selectedDf$Std_PP_2_Straight <- NULL
# selectedDf$Std_PP_2_Turning <- NULL
# selectedDf$Std_PP_3_Straight <- NULL
# selectedDf$Std_PP_3_Turning <- NULL
# 
# # According to Linear model
# selectedDf$PP_Dev_2_Straight <- abs(selectedDf$PP_Dev_2_Straight)
# selectedDf$PP_Dev_3_Straight <- abs(selectedDf$PP_Dev_3_Straight)
# selectedDf$PP_Dev_2_Turning <- abs(selectedDf$PP_Dev_2_Turning)
# selectedDf$PP_Dev_3_Turning <- abs(selectedDf$PP_Dev_3_Turning)
# selectedDf$PP_Prior <- abs(selectedDf$PP_Prior) # NULL

selectedDf$Class <- ifelse(selectedDf$PP_After >= thresholdPPAfter, T, F)
selectedDf$PP_After <- NULL

print(names(selectedDf))
 [1] "PP_Prior"          "Mean_PP_2_AccHigh" "Mean_PP_3_AccHigh" "Mean_PP_2_AccLow"  "Mean_PP_3_AccLow"  "Std_PP_2_AccHigh" 
 [7] "Std_PP_3_AccHigh"  "Std_PP_2_AccLow"   "Std_PP_3_AccLow"   "Activity_NO"       "Activity_C"        "Activity_M"       
[13] "Class"            
# library(mefa)
# combinedDf <- rep(combinedDf, 10) 
set.seed(39)
n_folds <- 5
params <- param <- list(objective       = "binary:logistic", 
               booster          = "gbtree",
               eval_metric      = "auc",
               eta              = 0.1,
               max_depth        = 10,
               alpha            = 1,
               lambda           = 0,
               gamma            = 0.45,
               min_child_weight = 0.3,
               subsample        = 0.5,
               colsample_bytree = 1)
           
# XGBoost Model         
xgb_m <- xgb.cv(   params               = param,
                  data = as.matrix(selectedDf %>% select(-Class)) ,
                  label =  selectedDf$Class,
                  nrounds             = 100,
                  verbose             = F,
                  prediction          = T,
                  maximize            = F, # Change this value to F will help to run with more itineration
                  nfold               = n_folds,
                  metrics             = c("auc", "error"),
                  early_stopping_rounds = 50,
                  stratified            = T,
                  scale_pos_weight      = 2)

# xgb_m$evaluation_log[xgb_m$best_iteration,"test_auc_mean"]
xgb_m$evaluation_log[xgb_m$best_iteration,]
NA

Performance Metrics

# Prediction
selectedDf$clsPred <- round(xgb_m$pred)

computePerformanceResults <- function(sdat){
  sdat = sdat[complete.cases(sdat),]
  acc = sum(sdat[,1] == sdat[,2])/nrow(sdat)
  conf_mat = table(sdat)
  specif = conf_mat[1,1]/sum(conf_mat[,1])
  sensiv = conf_mat[2,2]/sum(conf_mat[,2])
  preci =  conf_mat[2,2]/sum(conf_mat[2,])
  npv =    conf_mat[1,1]/sum(conf_mat[1,])
  return(c(acc,specif,sensiv,preci,npv))
}

# Get average performance
performance <- computePerformanceResults(selectedDf %>% select(Class, clsPred))
acc <- performance[1]
prec <- performance[4]
recall <- performance[3]
spec <- performance[2]
npv <- performance[5]
f1 <- (2 * recall * prec) / (recall + prec)
auc <- as.numeric(xgb_m$evaluation_log[xgb_m$best_iteration, "test_auc_mean"])

print(paste("Accuracy=", round(acc, 2)))
[1] "Accuracy= 0.87"
print(paste("Precision=", round(prec, 2)))
[1] "Precision= 0.83"
print(paste("Recall=", round(recall, 2)))
[1] "Recall= 0.86"
print(paste("Specificity=", round(spec, 2)))
[1] "Specificity= 0.88"
print(paste("NPV=", round(npv, 2)))
[1] "NPV= 0.9"
print(paste("F1=", round(f1, 2)))
[1] "F1= 0.85"
print(paste("AUC=", round(auc, 2)))
[1] "AUC= 0.9"
# Importance
bst <- xgboost(   params               = param,
                  data = as.matrix(selectedDf %>% select(-c(Class, clsPred))) ,
                  label =  selectedDf$Class,
                  nrounds             = 100,
                  verbose             = F,
                  prediction          = T,
                  maximize            = F, # Change this value to F will help to run with more itineration
                  nfold               = n_folds,
                  metrics             = c("auc", "error"),
                  early_stopping_rounds = 50,
                  stratified            = T,
                  scale_pos_weight      = 1)
importanceDf <- xgb.importance(colnames(selectedDf %>% select(-c(Class, clsPred))), model = bst)
print(importanceDf)
library(pROC)

dfROC <- pROC::roc(response = ifelse(selectedDf$Class==T, 1, 0),
               predictor = round(xgb_m$pred),
               levels=c(0, 1), direction = "<")

# it = which.max(xgb_m$evaluation_log$test_auc_mean)
# best.iter = xgb_m$evaluation_log$iter[it]
# best.iter 

plot(pROC::roc(response = ifelse(selectedDf$Class==T, 1, 0),
               predictor = round(xgb_m$pred),
               levels=c(0, 1), direction = "<"), 
     legacy.axes = TRUE,
     main="ROC Curve", 
     lwd=1.5) 

Plot feature importance

yAxis <- list(
  title = 'Importance',
  range=c(0.0, 1.0)
)
xAxis <- list(
  title = ''
)

importanceDf$Feature <- factor(importanceDf$Feature, levels = importanceDf[order(-Gain),]$Feature)
fig_Importance <- plot_ly(importanceDf, x = ~Feature, y = ~Gain, type = 'bar', name = 'Gain', width=600) %>%
  add_trace(y = ~Cover, name = 'Cover') %>% 
  add_trace(y = ~Frequency, name = 'Frequency') %>% 
  layout(yaxis = yAxis, xaxis=xAxis, barmode = 'group', title="Feature Importance") %>% 
  config(.Last.value, mathjax = 'cdn')

htmltools::tagList(fig_Importance)
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CnNvdXJjZSgnLi4vc2V0dGluZ3Mvc2V0dGluZ3MuUicpCnNvdXJjZSgnY29tbW9uRnVuY3Rpb25zLlInKQpsaWJyYXJ5KG5sbWUpCmBgYAoKYGBge3J9CnNldC5zZWVkKDQzKQpkcml2ZTEgPC0gcmVhZC5jc3YoJy4uL2RhdGEvcHJvY2Vzc2VkL2FuYWx5c2lzL1RUMV9Ecml2ZV8xX1BQLmNzdicpCmRyaXZlMiA8LSByZWFkLmNzdignLi4vZGF0YS9wcm9jZXNzZWQvQW5hbHlzaXMvVFQxX0RyaXZlXzJfUFAuY3N2JykKZHJpdmUzIDwtIHJlYWQuY3N2KCcuLi9kYXRhL3Byb2Nlc3NlZC9BbmFseXNpcy9UVDFfRHJpdmVfM19QUC5jc3YnKQpkcml2ZTQgPC0gcmVhZC5jc3YoJy4uL2RhdGEvcHJvY2Vzc2VkL0FuYWx5c2lzL1RUMV9Ecml2ZV80X1BQLmNzdicsIHN0cmluZ3NBc0ZhY3RvcnMgPSBUKQpgYGAKCmBgYHtyfQpkZlNlZyA8LSBkYXRhLmZyYW1lKHJlcCgxLCBucm93KGRyaXZlNCkpLCByZXAoMiwgbnJvdyhkcml2ZTQpKSwgcmVwKDMsIG5yb3coZHJpdmU0KSksIHJlcCg0LCBucm93KGRyaXZlNCkpKQpuYW1lcyhkZlNlZykgPC0gYygiU2VnMSIsICJTZWcyIiwgIlNlZzMiLCAiU2VnNCIpCgpjb21iaW5lZERmX1NlZzEgPC0gY2JpbmQoZHJpdmU0LCAKICAgICAgICAgICAgICAgICAgICBkcml2ZTEkTWVhblBQX1NlZzAsIAogICAgICAgICAgICAgICAgICAgIGRyaXZlMiRNZWFuUFBfU2VnMSwgZHJpdmUzJE1lYW5QUF9TZWcxLCAKICAgICAgICAgICAgICAgICAgICBkcml2ZTIkTWVhblBQX1NlZzBfMSwgZHJpdmUzJE1lYW5QUF9TZWcwXzEsCiAgICAgICAgICAgICAgICAgICAgZHJpdmUyJFN0ZFBQX1NlZzEsIGRyaXZlMyRTdGRQUF9TZWcxLAogICAgICAgICAgICAgICAgICAgIGRyaXZlMiRTdGRQUF9TZWcwXzEsIGRyaXZlMyRTdGRQUF9TZWcwXzEsCiAgICAgICAgICAgICAgICAgICAgZHJpdmUyJE1lYW5QUF9BY2NIaWdoMSwgZHJpdmUzJE1lYW5QUF9BY2NIaWdoMSwKICAgICAgICAgICAgICAgICAgICBkcml2ZTIkWC5NZWFuUFBfQWNjTG93MSwgZHJpdmUzJFguTWVhblBQX0FjY0xvdzEsCiAgICAgICAgICAgICAgICAgICAgZHJpdmUyJFN0ZFBQX0FjY0hpZ2gxLCBkcml2ZTMkU3RkUFBfQWNjSGlnaDEsCiAgICAgICAgICAgICAgICAgICAgZHJpdmUyJFN0ZFBQX0FjY0xvdzEsIGRyaXZlMyRTdGRQUF9BY2NMb3cxLAogICAgICAgICAgICAgICAgICAgIGRmU2VnJFNlZzEKICAgICAgICAgICAgICAgICAgKQpjb21iaW5lZERmX1NlZzIgPC0gY2JpbmQoZHJpdmU0LCAKICAgICAgICAgICAgICAgICAgICBkcml2ZTEkTWVhblBQX1NlZzAsIAogICAgICAgICAgICAgICAgICAgIGRyaXZlMiRNZWFuUFBfU2VnMiwgZHJpdmUzJE1lYW5QUF9TZWcyLCAKICAgICAgICAgICAgICAgICAgICBkcml2ZTIkTWVhblBQX1NlZzBfMiwgZHJpdmUzJE1lYW5QUF9TZWcwXzIsCiAgICAgICAgICAgICAgICAgICAgZHJpdmUyJFN0ZFBQX1NlZzIsIGRyaXZlMyRTdGRQUF9TZWcyLAogICAgICAgICAgICAgICAgICAgIGRyaXZlMiRTdGRQUF9TZWcwXzIsIGRyaXZlMyRTdGRQUF9TZWcwXzIsCiAgICAgICAgICAgICAgICAgICAgZHJpdmUyJE1lYW5QUF9BY2NIaWdoMiwgZHJpdmUzJE1lYW5QUF9BY2NIaWdoMiwKICAgICAgICAgICAgICAgICAgICBkcml2ZTIkWC5NZWFuUFBfQWNjTG93MiwgZHJpdmUzJFguTWVhblBQX0FjY0xvdzIsCiAgICAgICAgICAgICAgICAgICAgZHJpdmUyJFN0ZFBQX0FjY0hpZ2gyLCBkcml2ZTMkU3RkUFBfQWNjSGlnaDIsCiAgICAgICAgICAgICAgICAgICAgZHJpdmUyJFN0ZFBQX0FjY0xvdzIsIGRyaXZlMyRTdGRQUF9BY2NMb3cyLAogICAgICAgICAgICAgICAgICAgIGRmU2VnJFNlZzIKICAgICAgICAgICAgICAgICAgKQpjb21iaW5lZERmX1NlZzMgPC0gY2JpbmQoZHJpdmU0LCAKICAgICAgICAgICAgICAgICAgICBkcml2ZTEkTWVhblBQX1NlZzAsIAogICAgICAgICAgICAgICAgICAgIGRyaXZlMiRNZWFuUFBfU2VnMywgZHJpdmUzJE1lYW5QUF9TZWczLCAKICAgICAgICAgICAgICAgICAgICBkcml2ZTIkTWVhblBQX1NlZzBfMywgZHJpdmUzJE1lYW5QUF9TZWcwXzMsCiAgICAgICAgICAgICAgICAgICAgZHJpdmUyJFN0ZFBQX1NlZzMsIGRyaXZlMyRTdGRQUF9TZWczLAogICAgICAgICAgICAgICAgICAgIGRyaXZlMiRTdGRQUF9TZWcwXzMsIGRyaXZlMyRTdGRQUF9TZWcwXzMsCiAgICAgICAgICAgICAgICAgICAgZHJpdmUyJE1lYW5QUF9BY2NIaWdoMywgZHJpdmUzJE1lYW5QUF9BY2NIaWdoMywKICAgICAgICAgICAgICAgICAgICBkcml2ZTIkWC5NZWFuUFBfQWNjTG93MywgZHJpdmUzJFguTWVhblBQX0FjY0xvdzMsCiAgICAgICAgICAgICAgICAgICAgZHJpdmUyJFN0ZFBQX0FjY0hpZ2gzLCBkcml2ZTMkU3RkUFBfQWNjSGlnaDMsCiAgICAgICAgICAgICAgICAgICAgZHJpdmUyJFN0ZFBQX0FjY0xvdzMsIGRyaXZlMyRTdGRQUF9BY2NMb3czLAogICAgICAgICAgICAgICAgICAgIGRmU2VnJFNlZzMKICAgICAgICAgICAgICAgICAgKQpjb21iaW5lZERmX1NlZzQgPC0gY2JpbmQoZHJpdmU0LCAKICAgICAgICAgICAgICAgICAgICBkcml2ZTEkTWVhblBQX1NlZzAsIAogICAgICAgICAgICAgICAgICAgIGRyaXZlMiRNZWFuUFBfU2VnNCwgZHJpdmUzJE1lYW5QUF9TZWc0LCAKICAgICAgICAgICAgICAgICAgICBkcml2ZTIkTWVhblBQX1NlZzBfNCwgZHJpdmUzJE1lYW5QUF9TZWcwXzQsCiAgICAgICAgICAgICAgICAgICAgZHJpdmUyJFN0ZFBQX1NlZzQsIGRyaXZlMyRTdGRQUF9TZWc0LAogICAgICAgICAgICAgICAgICAgIGRyaXZlMiRTdGRQUF9TZWcwXzQsIGRyaXZlMyRTdGRQUF9TZWcwXzQsCiAgICAgICAgICAgICAgICAgICAgZHJpdmUyJE1lYW5QUF9BY2NIaWdoNCwgZHJpdmUzJE1lYW5QUF9BY2NIaWdoNCwKICAgICAgICAgICAgICAgICAgICBkcml2ZTIkWC5NZWFuUFBfQWNjTG93NCwgZHJpdmUzJFguTWVhblBQX0FjY0xvdzQsCiAgICAgICAgICAgICAgICAgICAgZHJpdmUyJFN0ZFBQX0FjY0hpZ2g0LCBkcml2ZTMkU3RkUFBfQWNjSGlnaDQsCiAgICAgICAgICAgICAgICAgICAgZHJpdmUyJFN0ZFBQX0FjY0xvdzQsIGRyaXZlMyRTdGRQUF9BY2NMb3c0LAogICAgICAgICAgICAgICAgICAgIGRmU2VnJFNlZzQKICAgICAgICAgICAgICAgICAgKQoKY29tbW9uX25hbWVzIDwtIGMoIlBQX0Rldl8xX1R1cm5pbmciLAogICAgICAgICAgICAgICAgICAiUFBfRGV2XzJfU3RyYWlnaHQiLCAiUFBfRGV2XzNfU3RyYWlnaHQiLCAKICAgICAgICAgICAgICAgICAgIlBQX0Rldl8yX1R1cm5pbmciLCAiUFBfRGV2XzNfVHVybmluZyIsIAogICAgICAgICAgICAgICAgICAiU3RkX1BQXzJfU3RyYWlnaHQiLCAiU3RkX1BQXzNfU3RyYWlnaHQiLCAKICAgICAgICAgICAgICAgICAgIlN0ZF9QUF8yX1R1cm5pbmciLCAiU3RkX1BQXzNfVHVybmluZyIsCiAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAiTWVhbl9QUF8yX0FjY0hpZ2giLCAiTWVhbl9QUF8zX0FjY0hpZ2giLAogICAgICAgICAgICAgICAgICAiTWVhbl9QUF8yX0FjY0xvdyIsICJNZWFuX1BQXzNfQWNjTG93IiwKICAgICAgICAgICAgICAgICAgIlN0ZF9QUF8yX0FjY0hpZ2giLCAiU3RkX1BQXzNfQWNjSGlnaCIsCiAgICAgICAgICAgICAgICAgICJTdGRfUFBfMl9BY2NMb3ciLCAiU3RkX1BQXzNfQWNjTG93IiwKICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICJTZWdtZW50IikKCm5hbWVzKGNvbWJpbmVkRGZfU2VnMSkgPC0gYyhuYW1lcyhkcml2ZTQpLCBjb21tb25fbmFtZXMpCm5hbWVzKGNvbWJpbmVkRGZfU2VnMikgPC0gYyhuYW1lcyhkcml2ZTQpLCBjb21tb25fbmFtZXMpCm5hbWVzKGNvbWJpbmVkRGZfU2VnMykgPC0gYyhuYW1lcyhkcml2ZTQpLCBjb21tb25fbmFtZXMpCm5hbWVzKGNvbWJpbmVkRGZfU2VnNCkgPC0gYyhuYW1lcyhkcml2ZTQpLCBjb21tb25fbmFtZXMpCgojIGNvbWJpbmVkRGZfU2VnMSRTdWJqZWN0IDwtIHBhc3RlMChhcy5mYWN0b3IoY29tYmluZWREZl9TZWcxJFN1YmplY3QpLCAiLlMxIikKIyBjb21iaW5lZERmX1NlZzIkU3ViamVjdCA8LSBwYXN0ZTAoYXMuZmFjdG9yKGNvbWJpbmVkRGZfU2VnMiRTdWJqZWN0KSwgIi5TMiIpCiMgY29tYmluZWREZl9TZWczJFN1YmplY3QgPC0gcGFzdGUwKGFzLmZhY3Rvcihjb21iaW5lZERmX1NlZzMkU3ViamVjdCksICIuUzMiKQojIGNvbWJpbmVkRGZfU2VnNCRTdWJqZWN0IDwtIHBhc3RlMChhcy5mYWN0b3IoY29tYmluZWREZl9TZWc0JFN1YmplY3QpLCAiLlM0IikKCmNvbWJpbmVkRGYgPC0gcmJpbmQoY29tYmluZWREZl9TZWcxLCBjb21iaW5lZERmX1NlZzIsIGNvbWJpbmVkRGZfU2VnMywgY29tYmluZWREZl9TZWc0KQoKIyBjb21iaW5lZERmJFN1YmplY3QgPC0gcGFzdGUwKCIjIiwgc3RyX3BhZChjb21iaW5lZERmJFN1YmplY3QsIDIsIHBhZD0iMCIpKQpjb21iaW5lZERmJFNlZ21lbnQgPC0gYXMuZmFjdG9yKGNvbWJpbmVkRGYkU2VnbWVudCkKY29tYmluZWREZiRBY3Rpdml0eUVuY29kZWQgPC0gZmFjdG9yKGlmZWxzZShjb21iaW5lZERmJEFjdGl2aXR5ID09ICJOTyIsICIxIiwgaWZlbHNlKGNvbWJpbmVkRGYkQWN0aXZpdHkgPT0gIkMiLCAiMiIsICIzIikpKQoKY29tYmluZWREZiA8LSBjb21iaW5lZERmW2NvbXBsZXRlLmNhc2VzKGNvbWJpbmVkRGYpLF0KY29tYmluZWREZiRTdWJqZWN0ID0gYXMuZmFjdG9yKGNvbWJpbmVkRGYkU3ViamVjdCkKYGBgCgoKYGBge3J9Cm1vZGVsID0gbG0oUFBfQWZ0ZXIgfiAKICAgICAgICAgICAgICBQUF9EZXZfMl9TdHJhaWdodCArIAogICAgICAgICAgICAgIFBQX0Rldl8zX1N0cmFpZ2h0ICsKICAgICAgICAgICAgICBQUF9EZXZfMl9UdXJuaW5nICsgCiAgICAgICAgICAgICAgUFBfRGV2XzNfVHVybmluZyArIAogICAgICAgICAgICAgIFN0ZF9QUF8yX1N0cmFpZ2h0ICsgCiAgICAgICAgICAgICAgU3RkX1BQXzNfU3RyYWlnaHQgKyAKICAgICAgICAgICAgICBTdGRfUFBfMl9UdXJuaW5nICsKICAgICAgICAgICAgICBTdGRfUFBfM19UdXJuaW5nICsKICAgICAgICAgICAgICAjIFBQX1ByaW9yICsKICAgICAgICAgICAgICBmYWN0b3IoQWN0aXZpdHlFbmNvZGVkKSwKICAgICAgICAgICAgZGF0YT1jb21iaW5lZERmLCByYW5kb20gPSB+MXxmYWN0b3IoU3ViamVjdCksIG1ldGhvZCA9ICJSRU1MIikKCiMgYW5vdmEobW9kZWwpCnN1bW1hcnkobW9kZWwpCnBsb3QobW9kZWwpCmBgYAoKIyBObyBSYW5kb20gRWZmZWN0cwpgYGB7cn0KbGluZWFyTW9kZWwxIDwtIGxtKFBQX0FmdGVyIH4gCiAgICAgICAgICAgICAgICBNZWFuX1BQXzJfQWNjSGlnaAogICAgICAgICAgICAgICsgTWVhbl9QUF8yX0FjY0xvdwogICAgICAgICAgICAgICsgTWVhbl9QUF8zX0FjY0hpZ2gKICAgICAgICAgICAgICArIE1lYW5fUFBfM19BY2NMb3cKICAgICAgICAgICAgICArIFN0ZF9QUF8yX0FjY0hpZ2gKICAgICAgICAgICAgICArIFN0ZF9QUF8yX0FjY0xvdwogICAgICAgICAgICAgICsgU3RkX1BQXzNfQWNjSGlnaAogICAgICAgICAgICAgICsgU3RkX1BQXzNfQWNjTG93CiAgICAgICAgICAgICAgIyArIFBQX1ByaW9yCiAgICAgICAgICAgICAgKyBmYWN0b3IoQWN0aXZpdHlFbmNvZGVkKSwKICAgICAgICAgICAgZGF0YT1jb21iaW5lZERmKQoKIyBhbm92YShtb2RlbCkKc3VtbWFyeShsaW5lYXJNb2RlbDEpCnBsb3QobGluZWFyTW9kZWwxKQpgYGAKCgojIFdpdGggUmFuZG9tIEVmZmVjdHMKYGBge3J9CmxpbmVhck1vZGVsMSA8LSBsbWUoUFBfQWZ0ZXIgfiAKICAgICAgICAgICAgICAgIE1lYW5fUFBfMl9BY2NIaWdoCiAgICAgICAgICAgICAgKyBNZWFuX1BQXzJfQWNjTG93CiAgICAgICAgICAgICAgKyBNZWFuX1BQXzNfQWNjSGlnaAogICAgICAgICAgICAgICsgTWVhbl9QUF8zX0FjY0xvdwogICAgICAgICAgICAgICsgU3RkX1BQXzJfQWNjSGlnaAogICAgICAgICAgICAgICsgU3RkX1BQXzJfQWNjTG93CiAgICAgICAgICAgICAgKyBTdGRfUFBfM19BY2NIaWdoCiAgICAgICAgICAgICAgKyBTdGRfUFBfM19BY2NMb3cKICAgICAgICAgICAgICAjICsgUFBfUHJpb3IKICAgICAgICAgICAgICArIGZhY3RvcihBY3Rpdml0eUVuY29kZWQpLAogICAgICAgICAgICBkYXRhPWNvbWJpbmVkRGYsIHJhbmRvbSA9IH4xfGZhY3RvcihTdWJqZWN0KSwgbWV0aG9kID0gIlJFTUwiKQoKIyBhbm92YShtb2RlbCkKc3VtbWFyeShsaW5lYXJNb2RlbDEpCnBsb3QobGluZWFyTW9kZWwxKQpgYGAKCiMjIE1hY2hpbmUgTGVhcm5pbmcKCmBgYHtyfQpwcEFmdGVyIDwtIGNvbWJpbmVkRGYkUFBfQWZ0ZXIKcHBBZnRlckFycmF5IDwtIG1hdHJpeChwcEFmdGVyLCBucm93ID0gMSxuY29sID0gbGVuZ3RoKHBwQWZ0ZXIpKQogIAp0aHJlc2hvbGRQUEFmdGVyIDwtIG90c3UocHBBZnRlckFycmF5LCByYW5nZT1jKG1pbihwcEFmdGVyKSwgbWF4KHBwQWZ0ZXIpKSkgIyBFeHBlY3RlZCBUaHJlc2hvbGQgPiAwLjEwMTIzCnByaW50KHBhc3RlMCgnVGhyZXNob2xkOiAnLCB0aHJlc2hvbGRQUEFmdGVyKSkKCnNlbGVjdGVkRGYgPC0gY29tYmluZWREZiAlPiUgc2VsZWN0KAogICAgICAgICAgICAgICAgICAiU3ViamVjdCIsICJBY3Rpdml0eSIsICJQUF9BZnRlciIsICJQUF9QcmlvciIsCiAgICAgICAgICAgICAgICAgICJNZWFuX1BQXzJfQWNjSGlnaCIsICJNZWFuX1BQXzNfQWNjSGlnaCIsCiAgICAgICAgICAgICAgICAgICJNZWFuX1BQXzJfQWNjTG93IiwgIk1lYW5fUFBfM19BY2NMb3ciLAogICAgICAgICAgICAgICAgICAiU3RkX1BQXzJfQWNjSGlnaCIsICJTdGRfUFBfM19BY2NIaWdoIiwKICAgICAgICAgICAgICAgICAgIlN0ZF9QUF8yX0FjY0xvdyIsICJTdGRfUFBfM19BY2NMb3ciKQoKc2VsZWN0ZWREZiRTdWJqZWN0IDwtIE5VTEwKc2VsZWN0ZWREZiRBY3Rpdml0eV9OTyA8LSBpZmVsc2Uoc2VsZWN0ZWREZiRBY3Rpdml0eSA9PSAiTk8iLCAxLCAwKQpzZWxlY3RlZERmJEFjdGl2aXR5X0MgPC0gaWZlbHNlKHNlbGVjdGVkRGYkQWN0aXZpdHkgPT0gIkMiLCAxLCAwKQpzZWxlY3RlZERmJEFjdGl2aXR5X00gPC0gaWZlbHNlKHNlbGVjdGVkRGYkQWN0aXZpdHkgPT0gIk0iLCAxLCAwKQpzZWxlY3RlZERmJEFjdGl2aXR5IDwtIE5VTEwKCiMgc2VsZWN0ZWREZiRQUF9EZXZfMV9UdXJuaW5nIDwtIE5VTEwKIyBzZWxlY3RlZERmJFN0ZF9QUF8yX1N0cmFpZ2h0IDwtIE5VTEwKIyBzZWxlY3RlZERmJFN0ZF9QUF8yX1R1cm5pbmcgPC0gTlVMTAojIHNlbGVjdGVkRGYkU3RkX1BQXzNfU3RyYWlnaHQgPC0gTlVMTAojIHNlbGVjdGVkRGYkU3RkX1BQXzNfVHVybmluZyA8LSBOVUxMCiMgCiMgIyBBY2NvcmRpbmcgdG8gTGluZWFyIG1vZGVsCiMgc2VsZWN0ZWREZiRQUF9EZXZfMl9TdHJhaWdodCA8LSBhYnMoc2VsZWN0ZWREZiRQUF9EZXZfMl9TdHJhaWdodCkKIyBzZWxlY3RlZERmJFBQX0Rldl8zX1N0cmFpZ2h0IDwtIGFicyhzZWxlY3RlZERmJFBQX0Rldl8zX1N0cmFpZ2h0KQojIHNlbGVjdGVkRGYkUFBfRGV2XzJfVHVybmluZyA8LSBhYnMoc2VsZWN0ZWREZiRQUF9EZXZfMl9UdXJuaW5nKQojIHNlbGVjdGVkRGYkUFBfRGV2XzNfVHVybmluZyA8LSBhYnMoc2VsZWN0ZWREZiRQUF9EZXZfM19UdXJuaW5nKQojIHNlbGVjdGVkRGYkUFBfUHJpb3IgPC0gYWJzKHNlbGVjdGVkRGYkUFBfUHJpb3IpICMgTlVMTAoKc2VsZWN0ZWREZiRDbGFzcyA8LSBpZmVsc2Uoc2VsZWN0ZWREZiRQUF9BZnRlciA+PSB0aHJlc2hvbGRQUEFmdGVyLCBULCBGKQpzZWxlY3RlZERmJFBQX0FmdGVyIDwtIE5VTEwKCnByaW50KG5hbWVzKHNlbGVjdGVkRGYpKQpgYGAKCmBgYHtyfQojIGxpYnJhcnkobWVmYSkKIyBjb21iaW5lZERmIDwtIHJlcChjb21iaW5lZERmLCAxMCkgCmBgYAoKYGBge3J9CnNldC5zZWVkKDM5KQpuX2ZvbGRzIDwtIDUKcGFyYW1zIDwtIHBhcmFtIDwtIGxpc3Qob2JqZWN0aXZlICAgICAgID0gImJpbmFyeTpsb2dpc3RpYyIsIAogICAgICAgICAgICAgICBib29zdGVyICAgICAgICAgID0gImdidHJlZSIsCiAgICAgICAgICAgICAgIGV2YWxfbWV0cmljICAgICAgPSAiYXVjIiwKICAgICAgICAgICAgICAgZXRhICAgICAgICAgICAgICA9IDAuMSwKICAgICAgICAgICAgICAgbWF4X2RlcHRoICAgICAgICA9IDEwLAogICAgICAgICAgICAgICBhbHBoYSAgICAgICAgICAgID0gMSwKICAgICAgICAgICAgICAgbGFtYmRhICAgICAgICAgICA9IDAsCiAgICAgICAgICAgICAgIGdhbW1hICAgICAgICAgICAgPSAwLjQ1LAogICAgICAgICAgICAgICBtaW5fY2hpbGRfd2VpZ2h0ID0gMC4zLAogICAgICAgICAgICAgICBzdWJzYW1wbGUgICAgICAgID0gMC41LAogICAgICAgICAgICAgICBjb2xzYW1wbGVfYnl0cmVlID0gMSkKICAgICAgICAgICAKIyBYR0Jvb3N0IE1vZGVsICAgICAgICAgCnhnYl9tIDwtIHhnYi5jdiggICBwYXJhbXMgICAgICAgICAgICAgICA9IHBhcmFtLAogICAgICAgICAgICAgICAgICBkYXRhID0gYXMubWF0cml4KHNlbGVjdGVkRGYgJT4lIHNlbGVjdCgtQ2xhc3MpKSAsCiAgICAgICAgICAgICAgICAgIGxhYmVsID0gIHNlbGVjdGVkRGYkQ2xhc3MsCiAgICAgICAgICAgICAgICAgIG5yb3VuZHMgICAgICAgICAgICAgPSAxMDAsCiAgICAgICAgICAgICAgICAgIHZlcmJvc2UgICAgICAgICAgICAgPSBGLAogICAgICAgICAgICAgICAgICBwcmVkaWN0aW9uICAgICAgICAgID0gVCwKICAgICAgICAgICAgICAgICAgbWF4aW1pemUgICAgICAgICAgICA9IEYsICMgQ2hhbmdlIHRoaXMgdmFsdWUgdG8gRiB3aWxsIGhlbHAgdG8gcnVuIHdpdGggbW9yZSBpdGluZXJhdGlvbgogICAgICAgICAgICAgICAgICBuZm9sZCAgICAgICAgICAgICAgID0gbl9mb2xkcywKICAgICAgICAgICAgICAgICAgbWV0cmljcyAgICAgICAgICAgICA9IGMoImF1YyIsICJlcnJvciIpLAogICAgICAgICAgICAgICAgICBlYXJseV9zdG9wcGluZ19yb3VuZHMgPSA1MCwKICAgICAgICAgICAgICAgICAgc3RyYXRpZmllZCAgICAgICAgICAgID0gVCwKICAgICAgICAgICAgICAgICAgc2NhbGVfcG9zX3dlaWdodCAgICAgID0gMikKCiMgeGdiX20kZXZhbHVhdGlvbl9sb2dbeGdiX20kYmVzdF9pdGVyYXRpb24sInRlc3RfYXVjX21lYW4iXQp4Z2JfbSRldmFsdWF0aW9uX2xvZ1t4Z2JfbSRiZXN0X2l0ZXJhdGlvbixdCgpgYGAKCiMjIFBlcmZvcm1hbmNlIE1ldHJpY3MKYGBge3J9CiMgUHJlZGljdGlvbgpzZWxlY3RlZERmJGNsc1ByZWQgPC0gcm91bmQoeGdiX20kcHJlZCkKCmNvbXB1dGVQZXJmb3JtYW5jZVJlc3VsdHMgPC0gZnVuY3Rpb24oc2RhdCl7CiAgc2RhdCA9IHNkYXRbY29tcGxldGUuY2FzZXMoc2RhdCksXQogIGFjYyA9IHN1bShzZGF0WywxXSA9PSBzZGF0WywyXSkvbnJvdyhzZGF0KQogIGNvbmZfbWF0ID0gdGFibGUoc2RhdCkKICBzcGVjaWYgPSBjb25mX21hdFsxLDFdL3N1bShjb25mX21hdFssMV0pCiAgc2Vuc2l2ID0gY29uZl9tYXRbMiwyXS9zdW0oY29uZl9tYXRbLDJdKQogIHByZWNpID0gIGNvbmZfbWF0WzIsMl0vc3VtKGNvbmZfbWF0WzIsXSkKICBucHYgPSAgICBjb25mX21hdFsxLDFdL3N1bShjb25mX21hdFsxLF0pCiAgcmV0dXJuKGMoYWNjLHNwZWNpZixzZW5zaXYscHJlY2ksbnB2KSkKfQoKIyBHZXQgYXZlcmFnZSBwZXJmb3JtYW5jZQpwZXJmb3JtYW5jZSA8LSBjb21wdXRlUGVyZm9ybWFuY2VSZXN1bHRzKHNlbGVjdGVkRGYgJT4lIHNlbGVjdChDbGFzcywgY2xzUHJlZCkpCmFjYyA8LSBwZXJmb3JtYW5jZVsxXQpwcmVjIDwtIHBlcmZvcm1hbmNlWzRdCnJlY2FsbCA8LSBwZXJmb3JtYW5jZVszXQpzcGVjIDwtIHBlcmZvcm1hbmNlWzJdCm5wdiA8LSBwZXJmb3JtYW5jZVs1XQpmMSA8LSAoMiAqIHJlY2FsbCAqIHByZWMpIC8gKHJlY2FsbCArIHByZWMpCmF1YyA8LSBhcy5udW1lcmljKHhnYl9tJGV2YWx1YXRpb25fbG9nW3hnYl9tJGJlc3RfaXRlcmF0aW9uLCAidGVzdF9hdWNfbWVhbiJdKQoKcHJpbnQocGFzdGUoIkFjY3VyYWN5PSIsIHJvdW5kKGFjYywgMikpKQpwcmludChwYXN0ZSgiUHJlY2lzaW9uPSIsIHJvdW5kKHByZWMsIDIpKSkKcHJpbnQocGFzdGUoIlJlY2FsbD0iLCByb3VuZChyZWNhbGwsIDIpKSkKcHJpbnQocGFzdGUoIlNwZWNpZmljaXR5PSIsIHJvdW5kKHNwZWMsIDIpKSkKcHJpbnQocGFzdGUoIk5QVj0iLCByb3VuZChucHYsIDIpKSkKcHJpbnQocGFzdGUoIkYxPSIsIHJvdW5kKGYxLCAyKSkpCnByaW50KHBhc3RlKCJBVUM9Iiwgcm91bmQoYXVjLCAyKSkpCmBgYAoKYGBge3J9CiMgSW1wb3J0YW5jZQpic3QgPC0geGdib29zdCggICBwYXJhbXMgICAgICAgICAgICAgICA9IHBhcmFtLAogICAgICAgICAgICAgICAgICBkYXRhID0gYXMubWF0cml4KHNlbGVjdGVkRGYgJT4lIHNlbGVjdCgtYyhDbGFzcywgY2xzUHJlZCkpKSAsCiAgICAgICAgICAgICAgICAgIGxhYmVsID0gIHNlbGVjdGVkRGYkQ2xhc3MsCiAgICAgICAgICAgICAgICAgIG5yb3VuZHMgICAgICAgICAgICAgPSAxMDAsCiAgICAgICAgICAgICAgICAgIHZlcmJvc2UgICAgICAgICAgICAgPSBGLAogICAgICAgICAgICAgICAgICBwcmVkaWN0aW9uICAgICAgICAgID0gVCwKICAgICAgICAgICAgICAgICAgbWF4aW1pemUgICAgICAgICAgICA9IEYsICMgQ2hhbmdlIHRoaXMgdmFsdWUgdG8gRiB3aWxsIGhlbHAgdG8gcnVuIHdpdGggbW9yZSBpdGluZXJhdGlvbgogICAgICAgICAgICAgICAgICBuZm9sZCAgICAgICAgICAgICAgID0gbl9mb2xkcywKICAgICAgICAgICAgICAgICAgbWV0cmljcyAgICAgICAgICAgICA9IGMoImF1YyIsICJlcnJvciIpLAogICAgICAgICAgICAgICAgICBlYXJseV9zdG9wcGluZ19yb3VuZHMgPSA1MCwKICAgICAgICAgICAgICAgICAgc3RyYXRpZmllZCAgICAgICAgICAgID0gVCwKICAgICAgICAgICAgICAgICAgc2NhbGVfcG9zX3dlaWdodCAgICAgID0gMSkKaW1wb3J0YW5jZURmIDwtIHhnYi5pbXBvcnRhbmNlKGNvbG5hbWVzKHNlbGVjdGVkRGYgJT4lIHNlbGVjdCgtYyhDbGFzcywgY2xzUHJlZCkpKSwgbW9kZWwgPSBic3QpCnByaW50KGltcG9ydGFuY2VEZikKYGBgCgpgYGB7cn0KbGlicmFyeShwUk9DKQoKZGZST0MgPC0gcFJPQzo6cm9jKHJlc3BvbnNlID0gaWZlbHNlKHNlbGVjdGVkRGYkQ2xhc3M9PVQsIDEsIDApLAogICAgICAgICAgICAgICBwcmVkaWN0b3IgPSByb3VuZCh4Z2JfbSRwcmVkKSwKICAgICAgICAgICAgICAgbGV2ZWxzPWMoMCwgMSksIGRpcmVjdGlvbiA9ICI8IikKCiMgaXQgPSB3aGljaC5tYXgoeGdiX20kZXZhbHVhdGlvbl9sb2ckdGVzdF9hdWNfbWVhbikKIyBiZXN0Lml0ZXIgPSB4Z2JfbSRldmFsdWF0aW9uX2xvZyRpdGVyW2l0XQojIGJlc3QuaXRlciAKCnBsb3QocFJPQzo6cm9jKHJlc3BvbnNlID0gaWZlbHNlKHNlbGVjdGVkRGYkQ2xhc3M9PVQsIDEsIDApLAogICAgICAgICAgICAgICBwcmVkaWN0b3IgPSByb3VuZCh4Z2JfbSRwcmVkKSwKICAgICAgICAgICAgICAgbGV2ZWxzPWMoMCwgMSksIGRpcmVjdGlvbiA9ICI8IiksIAogICAgIGxlZ2FjeS5heGVzID0gVFJVRSwKICAgICBtYWluPSJST0MgQ3VydmUiLCAKICAgICBsd2Q9MS41KSAKYGBgCgoKIyMjIFBsb3QgZmVhdHVyZSBpbXBvcnRhbmNlCmBgYHtyfQp5QXhpcyA8LSBsaXN0KAogIHRpdGxlID0gJ0ltcG9ydGFuY2UnLAogIHJhbmdlPWMoMC4wLCAxLjApCikKeEF4aXMgPC0gbGlzdCgKICB0aXRsZSA9ICcnCikKCmltcG9ydGFuY2VEZiRGZWF0dXJlIDwtIGZhY3RvcihpbXBvcnRhbmNlRGYkRmVhdHVyZSwgbGV2ZWxzID0gaW1wb3J0YW5jZURmW29yZGVyKC1HYWluKSxdJEZlYXR1cmUpCmZpZ19JbXBvcnRhbmNlIDwtIHBsb3RfbHkoaW1wb3J0YW5jZURmLCB4ID0gfkZlYXR1cmUsIHkgPSB+R2FpbiwgdHlwZSA9ICdiYXInLCBuYW1lID0gJ0dhaW4nLCB3aWR0aD02MDApICU+JQogIGFkZF90cmFjZSh5ID0gfkNvdmVyLCBuYW1lID0gJ0NvdmVyJykgJT4lIAogIGFkZF90cmFjZSh5ID0gfkZyZXF1ZW5jeSwgbmFtZSA9ICdGcmVxdWVuY3knKSAlPiUgCiAgbGF5b3V0KHlheGlzID0geUF4aXMsIHhheGlzPXhBeGlzLCBiYXJtb2RlID0gJ2dyb3VwJywgdGl0bGU9IkZlYXR1cmUgSW1wb3J0YW5jZSIpICU+JSAKICBjb25maWcoLkxhc3QudmFsdWUsIG1hdGhqYXggPSAnY2RuJykKCmh0bWx0b29sczo6dGFnTGlzdChmaWdfSW1wb3J0YW5jZSkKYGBgCgoK